package cn.trigram.config.filter;

import cn.trigram.config.filter.wrapper.ReuseRequestWrapper;
import cn.trigram.config.filter.wrapper.ReuseResponseWrapper;
import java.io.IOException;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.Collection;
import java.util.Enumeration;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.filter.OrderedFilter;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.util.StopWatch;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

@Slf4j
public class LogRequestFilter extends HttpFilter implements OrderedFilter {

  @Autowired
  private RequestMappingHandlerMapping requestMappingHandlerMapping;

  @Override
  protected void doFilter(
      HttpServletRequest request, HttpServletResponse response,
      FilterChain filterChain
  ) throws IOException, ServletException {

    log.info("\n========================================== 请求开始 ==========================================");
    log.info("URL：{}", request.getRequestURL());
    // 过滤器放行并计时
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    filterChain.doFilter(request, response);
    stopWatch.stop();
    try {
      ReuseRequestWrapper  requestWrapper  = (ReuseRequestWrapper) request;
      ReuseResponseWrapper responseWrapper = (ReuseResponseWrapper) response;
      String               contentType     = request.getContentType();
      // 执行时间毫秒单位
      // 打印请求 url、Http method、controller 的全路径以及执行方法、请求的IP、请求入参
      Map<String, Object> paramMap = new LinkedHashMap<>(8);
      paramMap.put("Time-Consuming(ms)", stopWatch.getLastTaskTimeMillis());
      paramMap.put("URL", requestWrapper.getRequestURL());
      paramMap.put("HTTP Method", requestWrapper.getMethod());
      {
        // 处理请求信息
        Map<String, Object> requestMap = new LinkedHashMap<>();
        // 未标注不打印日志的请求，才需要处理请求参数
        String                queryString  = URLDecoder.decode(
            Optional.ofNullable(requestWrapper.getQueryString()).orElse(""), StandardCharsets.UTF_8.name());
        Map<String, String[]> parameterMap = requestWrapper.getParameterMap();
        String                body         = new String(requestWrapper.getContentAsByteArray(), StandardCharsets.UTF_8);
        // url地址的参数
        requestMap.put("QueryString", queryString);
        // 包括url地址的和form表单的，所以减去上面的部分就是表单的
        requestMap.put("ParameterMap", parameterMap);
        // 请求正文的
        requestMap.put("Body", body);
        if (Objects.nonNull(contentType) && (
            contentType.startsWith(MediaType.MULTIPART_FORM_DATA_VALUE)
            || contentType.startsWith(MediaType.MULTIPART_MIXED_VALUE)
            || contentType.startsWith(MediaType.MULTIPART_RELATED_VALUE)
        )) {
          final Map<String, List<String>> fileMaps = requestWrapper.getParts().stream()
              .filter(x -> StringUtils.hasLength(x.getSubmittedFileName()))
              .collect(Collectors.groupingBy(
                  Part::getName,
                  Collectors.mapping(
                      v -> String.format("filename[%s],size[%s]", v.getSubmittedFileName(), v.getSize()),
                      Collectors.toList()
                  )
              ));
          // 上传文件的，只显示名字和大小
          requestMap.put("MultiFileParts", fileMaps);
        }

        //读取请求头
        Map<String, Object> headerMap   = new LinkedHashMap<>();
        Enumeration<String> headerNames = requestWrapper.getHeaderNames();
        while (headerNames.hasMoreElements()) {
          String headerName  = headerNames.nextElement();
          String headerValue = requestWrapper.getHeader(headerName);
          //sessionId去掉
          if (headerName.equalsIgnoreCase("session-id")) {
            int index = headerValue.lastIndexOf(":");
            if (index > 0) {
              headerValue = headerValue.substring(0, index);
            }
          }
          headerMap.put(headerName, headerValue);
        }
        requestMap.put("Headers", headerMap);
        paramMap.put("Request", requestMap);
      }
      {
        //处理响应信息
        Map<String, Object> responseMap         = new LinkedHashMap<>();
        String              responseContentType = responseWrapper.getContentType();
        if (Objects.nonNull(responseContentType)
            && responseContentType.startsWith(MediaType.APPLICATION_OCTET_STREAM_VALUE)) {
          // 暂时application/octet-stream响应内容就是文件流，不打印文件的二进制
          responseMap.put("Attachment-File-Name", responseWrapper.getHeader(HttpHeaders.CONTENT_DISPOSITION));
          responseMap.put("Attachment-File-Size(Byte)", responseWrapper.getContentSize());
        }
        //读取响应头
        Map<String, Object> headerMap   = new LinkedHashMap<>();
        Collection<String>  headerNames = response.getHeaderNames();
        for (String headerName : headerNames) {
          headerMap.put(headerName, response.getHeader(headerName));
        }
        responseMap.put("Headers", headerMap);
        paramMap.put("Response", responseMap);
      }
      paramMap.put("IP", requestWrapper.getRemoteAddr());
      log.info("{}", paramMap);
    } catch (Throwable throwable) {
      log.error("日志记录过滤器错误：", throwable);
    }
    log.info("\n=========================================== 请求结束 ===========================================\n");
  }

  @Override
  public int getOrder() {

    return OrderedFilter.REQUEST_WRAPPER_FILTER_MAX_ORDER - 106;
  }

}
